/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "AreaBuilder.h"
#include "TaskQueue.h"
#include "Respawner.h"

#include "Area.h"
#include "Room.h"
#include "MudConsumable.h"
#include "MudContainer.h"
#include "MudCreature.h"
#include "MudCredits.h"
#include "MudDoor.h"
#include "MudExit.h"
#include "MudItem.h"
#include "MudObject.h"
#include "MudKey.h"
#include "MudWearable.h"
#include "TrainTask.h"
#include "InstancePortal.h"
#include "config.h"
#include "MudCreatureConductorTask.h"
#include "ElevatorController.h"
#include "ItemDb.h"
#include "Effects.h"
#include "Global.h"

namespace Chronos {

class ConductorFactory : public CreatureFactory {
public:
    ConductorFactory(const MudCreature& t) : CreatureFactory(t) {} 
    virtual MudObject& create() const {
        MudCreature& creature = (MudCreature&) CreatureFactory::create();
        Global<TaskQueue>()->addTask(new MudCreatureConductorTask(creature), 1);
        return creature;
    }
};

}

enum ItemPrototypes {
    Item_basic = 0,
    Item_rock = 1,
    Item_hidden = 2,
    Item_money100 = 3,
    Item_beer = 4,
    Item_food = 5,
    Item_helmet = 6,
    Item_hat = 7,
    Item_pants = 8,
    Item_jewel = 9,
    Item_key = 10,
};

using namespace Chronos;
using namespace std;

AreaBuilder::AreaBuilder(TaskQueue& taskQueue_)
    : taskQueue(taskQueue_)
{
}


void AreaBuilder::loadItems() {
    ItemDb* items = Global<ItemDb>();
    MudItem basic(Item_basic, "basic item", "a basic item you can pickup and drop.", 1);
    basic.addAlias("basic");
    basic.addAlias("item");
    items->addItem(Item_basic, new MudItemFactory(basic));

    MudItem rockProto(Item_rock, "respawning rock", "a respawning rock.", 3);
    rockProto.addAlias("rock");
    items->addItem(Item_rock, new MudItemFactory(rockProto));

    MudItem hiddenProto(Item_hidden, "hidden", "a hidden item.", 2);
    hiddenProto.setVisible(false);
    items->addItem(Item_hidden, new MudItemFactory(hiddenProto));

    MudCredits credits(Item_money100, "money", "respawning money. Picking it up will add it to your total credits.", 100);
    items->addItem(Item_money100, new MudCreditsFactory(credits));

    MudDrink beerProto(Item_beer, "beer", "The real thing. Please drink me.", 1, instantHealingEffect);
    items->addItem(Item_beer, new MudDrinkFactory(beerProto));

    MudFood foodProto(Item_food, "hamburger", "The real thing. Please eat me.", 1, instantHealingEffect);
    items->addItem(Item_food, new MudFoodFactory(foodProto));

    MudWearable helmet(Item_helmet, "helmet", "a steel helmet. It has +10 health.", 2, Equipment::head);
    helmet.addModifier(Stats::maxHealth, 10);
    items->addItem(Item_helmet, new MudWearableFactory(helmet));

    MudWearable hat(Item_hat, "hat", "A hat without modifiers.", 1, Equipment::head);
    items->addItem(Item_hat, new MudWearableFactory(hat));

    MudWearable pants(Item_pants, "pants", "Pants to go into the leg slot.", 1, Equipment::leg);
    items->addItem(Item_pants, new MudWearableFactory(pants));
}


Area* AreaBuilder::getArea1() const {
    ItemDb* items = Global<ItemDb>();
    Area* area = new Area(1, "demo area");

    // central room
    Room* centralRoom = new Room("Basic room", "this room shows a basic room. A stairway leads up..");
	centralRoom->addObject(new MudNorthExit(*area, 5));
	centralRoom->addObject(new MudEastExit(*area, 2));
	centralRoom->addObject(new MudSouthExit(*area, 3));
	centralRoom->addObject(new MudWestExit(*area, 4));
	centralRoom->addObject(new MudUpExit(*area, 6));
    area->addRoom(1, *centralRoom);

    // object room
    Room* objectRoom = new Room("Object room", "this room shows objects of various types.");
    MudObject* object = new MudObject("object", "a bare object. You can only look at it. There is a 'hidden' also. There"
                " is also an alias 'object2' that points to the same object.");
    object->addAlias("object2");
    objectRoom->addObject(object);
    MudObject* hidden = new MudObject("hidden", "A hidden, bare object.");
    hidden->setVisible(false);
    objectRoom->addObject(hidden);
	objectRoom->addObject(new MudWestExit(*area, 1));
    area->addRoom(2, *objectRoom);

    // container room
    Room* containerRoom = new Room("Container room", "this room shows various container objects.");
    containerRoom->addObject(new MudContainer("container", "The basic Container."));
    containerRoom->addObject(new MudOpenableContainer("chest", "This openable container auto-closes after 10 seconds.", seconds(10)));
    MudLockedContainer* lc = new MudLockedContainer("safe", "A locked container with a key. It auto-closes/locks after 10 seconds.", seconds(10));
    containerRoom->addObject(lc);
    containerRoom->addObject(new MudKey(Item_key, "key", "The key to the safe key.", lc->getKeyID()));
    containerRoom->addObject(new MudKey(Item_key, "wrong key", "The wrong key.", lc->getKeyID()+1));
    MudCredits money(Item_money100, "money", "The money respawns inside the safe.", 100);
    new ItemContainerRespawner(new MudCreditsFactory(money), lc, seconds(10));
	containerRoom->addObject(new MudNorthExit(*area, 1));
    area->addRoom(3, *containerRoom);

    // item room
    Room* itemRoom = new Room("Item room", "this room shows various item types. Try finding the hidden item.");
    new ItemRespawner(new ItemDbFactory(*items, Item_basic), itemRoom, seconds(20));
    new ItemRespawner(new ItemDbFactory(*items, Item_rock), itemRoom, seconds(10), "the rock respawns.");
    new ItemRespawner(new ItemDbFactory(*items, Item_hidden), itemRoom, seconds(10));
    new ItemRespawner(new ItemDbFactory(*items, Item_money100), itemRoom, seconds(10), "more money appears.");
    new ItemRespawner(new ItemDbFactory(*items, Item_beer), itemRoom, seconds(20), "More beer!");
    new ItemRespawner(new ItemDbFactory(*items, Item_food), itemRoom, seconds(20), "More food!");
	itemRoom->addObject(new MudEastExit(*area, 1));
    area->addRoom(4, *itemRoom);

    // creature room
    Room* creatureRoom = new Room("Creature room", "this room shows creatures. Try attacking one.");
    MudCreature fishProto("fish", "A small, blue fish.", 20, 20, 3, 80);
    fishProto.addChat("fish says: 'blup'", 1);
    fishProto.addChat("fish swims around in circles.", 1);
    new CreatureRespawner(new CreatureFactory(fishProto), creatureRoom, seconds(20), "a fish appears.");
    MudCreature pitbullProto("pitbull", "A very aggressive  dog.", 10, 20, 3, 80, true);
    new CreatureRespawner(new CreatureFactory(pitbullProto), creatureRoom, seconds(30), "a red-eyed pitbull runs up to you.");
    MudCreature giantProto("giant", "He looks dangerous.", 100, 15, 20, 90);
    new CreatureRespawner(new CreatureFactory(giantProto), creatureRoom, seconds(20), "a giant crawls out of the ground.");
	creatureRoom->addObject(new MudSouthExit(*area, 1));
    area->addRoom(5, *creatureRoom);

    // central room (2)
    Room* centralRoom2 = new Room("Basic room2", "A stairway leads up and down..");
	centralRoom2->addObject(new MudNorthExit(*area, 8));
	centralRoom2->addObject(new MudEastExit(*area, 7));
	centralRoom2->addObject(new MudSouthExit(*area, 10));
	centralRoom2->addObject(new MudWestExit(*area, 12));
	centralRoom2->addObject(new MudDownExit(*area, 1));
	centralRoom2->addObject(new MudUpExit(*area, 16));
    area->addRoom(6, *centralRoom2);

    // door room
    Room* doorRoom = new Room("Door room", "Try passing through the doors'.");
	doorRoom->addObject(new MudDoor("door", "a door is an openable exit.", new RoomPortal(*area, 9, "arrives", "leaves", "you enter"), seconds(10)));
    MudLockedDoor* ldoor = new MudLockedDoor("safe", "a locked door.", new RoomPortal(*area, 9, "", "", ""), seconds(10));
    doorRoom->addObject(new MudKey(Item_key, "key", "The key to the safe key.", ldoor->getKeyID()));
    doorRoom->addObject(new MudKey(Item_key, "wrong key", "The wrong key to the safe key.", ldoor->getKeyID()+1));
    doorRoom->addObject(ldoor);
    doorRoom->addObject(new MudWestExit(*area, 6));
	doorRoom->addObject(new MudExit("portal", "This portal leads to another area.", new AreaPortal(2, 1, " arrives.", " leaves.", "you enter.")));
    area->addRoom(7, *doorRoom);
    Room* doorRoom2 = new Room("Door room (2)", "You did it!");
    doorRoom2->addObject(new MudWestExit(*area, 7));
    area->addRoom(9, *doorRoom2);

    // equipment room
    Room* equipmentRoom = new Room("Equipment room", "Try wearing/removing equipment. Also use 'equip'.");
    new ItemRespawner(new ItemDbFactory(*items, Item_helmet), equipmentRoom, seconds(20), "pop.");
    new ItemRespawner(new ItemDbFactory(*items, Item_hat), equipmentRoom, seconds(30), "pop.");
    new ItemRespawner(new ItemDbFactory(*items, Item_pants), equipmentRoom, seconds(25), "pop.");
    equipmentRoom->addObject(new MudSouthExit(*area, 6));
    area->addRoom(8, *equipmentRoom);

    // trigger room
    Room* triggerRoom = new Room("Trigger room", "Try going south. The guard will warn you, the bouncer attacks");
    triggerRoom->addObject(new MudNorthExit(*area, 6));
    MudObject* triggerExit = new MudSouthExit(*area, 11);
    triggerExit->addTrigger(MUDEXIT_PASS, "guard", false, MESSAGE, "The friendly guard warns you.\n");
    triggerExit->addTrigger(MUDEXIT_PASS, "bouncer", true, ATTACK, "The bouncer says: 'He warned you!'\n");
    triggerRoom->addObject(triggerExit);
    MudCreature guardProto("guard", "He guards the South exit.", 20, 20, 20, 20);
    new CreatureRespawner(new CreatureFactory(guardProto), triggerRoom, seconds(15), "a new guard enters the room.");
    MudCreature bouncerProto("bouncer", "He guards the South exit.", 10, 20, 15, 50);
    new CreatureRespawner(new CreatureFactory(bouncerProto), triggerRoom, seconds(30), "a new bouncer arrives.");
    MudItem* triggerItem = new MudItem(Item_jewel, "jewel", "it's guarded. Try picking it up.", 1);
    triggerItem->addTrigger(MUDITEM_TRY_PICKUP, "guard", true, MESSAGE, "The guard pushes you away.\n");
    triggerRoom->addObject(triggerItem);
    area->addRoom(10, *triggerRoom);
    Room* triggerRoom2 = new Room("Trigger room (2)", "Congratulations, you got past the guard!");
    triggerRoom2->addObject(new MudNorthExit(*area, 10));
    area->addRoom(11, *triggerRoom2);

    // elevator room
    Room* elevatorRoom = new Room("Elevator room - Floor 1", "There are 3 levels. Visit them all.");
    area->addRoom(12, *elevatorRoom);
	area->addRoom(13, *new Room("Floor 2", ""));
	area->addRoom(14, *new Room("Floor 5", ""));
    Room* elevator = new Room("Elevator", "");
	elevatorRoom->addObject(new MudEastExit(*area, 6));
    area->addRoom(15, *elevator);
	MudRoomExit* elevatorEntry = new MudRoomExit("elevator", "", "", "", "", *area, 15);
	ElevatorController* elevatorController = new ElevatorController(*elevator, elevatorEntry, *area);
	elevatorController->addFloor(5, 14);
	elevatorController->addFloor(2, 13);
	elevatorController->addFloor(1, 12);
	area->controller = elevatorController;
    
    // central room (3)
    Room* centralRoom3 = new Room("Basic room3", "A stairway leads down..");
	centralRoom3->addObject(new MudNorthExit(*area, 17));
	centralRoom3->addObject(new MudEastExit(*area, 20));
	centralRoom3->addObject(new MudDownExit(*area, 6));
    area->addRoom(16, *centralRoom3);

    // train room
    Room* trainRoom = new Room("Train room", "A station. The train will come periodically.");
	trainRoom->addObject(new MudSouthExit(*area, 16));
    area->addRoom(17, *trainRoom);
    Room* trainRoom2 = new Room("Train room (2)", "A station. The train will come periodically.");
    area->addRoom(18, *trainRoom2);
	Room* train = new Room("Train", "The train is completely empty at this hour.");
    area->addRoom(19, *train);
    MudExit* trainEntry =  new MudRoomExit("train", "It's the TGV.", " steps inside.", " enters the train.", "you enter the train.", *area, 19);
	TrainTask* trainTask = new TrainTask(*area, 19, trainEntry);
	trainTask->addDestRoom(17, new MudRoomExit("out", "The train's exit", " steps out of the train.", " steps out of the train.", "you leave the train.", *area, 17));
	trainTask->addDestRoom(18, new MudRoomExit("out", "The train's exit", " steps out of the train.", " steps out of the train.", "you leave the train.", *area, 18));
	taskQueue.addTask(trainTask, 3);

    Room* instanceEntryRoom = new Room("Instance entrance", "A shimmering blue portal stands here.");
	instanceEntryRoom->addObject(new MudWestExit(*area, 16));
    instanceEntryRoom->addObject(new MudExit("portal", "it leads to an instance.", new InstancePortal("", "", "")));
    area->addRoom(20, *instanceEntryRoom);

    return area;
}


Area* AreaBuilder::getArea2() const {
    Area* area = new Area(2, "test area #2");

    Room* room1 = new Room("Room#1", "room no 1");
	room1->addObject(new MudSouthExit(*area, 2));
	room1->addObject(new MudExit("void", "a black hole.", new AreaPortal(1, 7, " arrives.", " leaves", "you enter.")));
    area->addRoom(1, *room1);

    Room* room2 = new Room("Room#2", "room no 2");
	room2->addObject(new MudNorthExit(*area, 1));
    area->addRoom(2, *room2);

    return area;
}

